// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package result

import (
	"errors"
	"fmt"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// TestTraverseArrayG_Success tests successful traversal of an array with all valid elements
func TestTraverseArrayG_Success(t *testing.T) {
	parse := func(s string) (int, error) {
		return strconv.Atoi(s)
	}

	result, err := TraverseArrayG[[]string, []int](parse)([]string{"1", "2", "3"})

	require.NoError(t, err)
	assert.Equal(t, []int{1, 2, 3}, result)
}

// TestTraverseArrayG_Error tests that traversal short-circuits on first error
func TestTraverseArrayG_Error(t *testing.T) {
	parse := func(s string) (int, error) {
		return strconv.Atoi(s)
	}

	result, err := TraverseArrayG[[]string, []int](parse)([]string{"1", "bad", "3"})

	require.Error(t, err)
	assert.Nil(t, result)
}

// TestTraverseArrayG_EmptyArray tests traversal of an empty array
func TestTraverseArrayG_EmptyArray(t *testing.T) {
	parse := func(s string) (int, error) {
		return strconv.Atoi(s)
	}

	result, err := TraverseArrayG[[]string, []int](parse)([]string{})

	require.NoError(t, err)
	assert.Empty(t, result)
	assert.NotNil(t, result) // Should be an empty slice, not nil
}

// TestTraverseArrayG_SingleElement tests traversal with a single element
func TestTraverseArrayG_SingleElement(t *testing.T) {
	parse := func(s string) (int, error) {
		return strconv.Atoi(s)
	}

	result, err := TraverseArrayG[[]string, []int](parse)([]string{"42"})

	require.NoError(t, err)
	assert.Equal(t, []int{42}, result)
}

// TestTraverseArrayG_ShortCircuit tests that processing stops at first error
func TestTraverseArrayG_ShortCircuit(t *testing.T) {
	callCount := 0
	parse := func(s string) (int, error) {
		callCount++
		if s == "error" {
			return 0, errors.New("parse error")
		}
		return len(s), nil
	}

	_, err := TraverseArrayG[[]string, []int](parse)([]string{"ok", "error", "should-not-process"})

	require.Error(t, err)
	assert.Equal(t, 2, callCount, "should stop after encountering error")
}

// TestTraverseArrayG_CustomSliceType tests with custom slice types
func TestTraverseArrayG_CustomSliceType(t *testing.T) {
	type StringSlice []string
	type IntSlice []int

	parse := func(s string) (int, error) {
		return strconv.Atoi(s)
	}

	input := StringSlice{"1", "2", "3"}
	result, err := TraverseArrayG[StringSlice, IntSlice](parse)(input)

	require.NoError(t, err)
	assert.Equal(t, IntSlice{1, 2, 3}, result)
}

// TestTraverseArray_Success tests successful traversal
func TestTraverseArray_Success(t *testing.T) {
	validate := func(s string) (int, error) {
		n, err := strconv.Atoi(s)
		if err != nil {
			return 0, err
		}
		if n < 0 {
			return 0, errors.New("negative number")
		}
		return n * 2, nil
	}

	result, err := TraverseArray(validate)([]string{"1", "2", "3"})

	require.NoError(t, err)
	assert.Equal(t, []int{2, 4, 6}, result)
}

// TestTraverseArray_ValidationError tests validation failure
func TestTraverseArray_ValidationError(t *testing.T) {
	validate := func(s string) (int, error) {
		n, err := strconv.Atoi(s)
		if err != nil {
			return 0, err
		}
		if n < 0 {
			return 0, errors.New("negative number")
		}
		return n * 2, nil
	}

	result, err := TraverseArray(validate)([]string{"1", "-5", "3"})

	require.Error(t, err)
	assert.Contains(t, err.Error(), "negative number")
	assert.Nil(t, result)
}

// TestTraverseArray_ParseError tests parse failure
func TestTraverseArray_ParseError(t *testing.T) {
	validate := func(s string) (int, error) {
		n, err := strconv.Atoi(s)
		if err != nil {
			return 0, err
		}
		return n, nil
	}

	result, err := TraverseArray(validate)([]string{"1", "not-a-number", "3"})

	require.Error(t, err)
	assert.Nil(t, result)
}

// TestTraverseArray_EmptyArray tests empty array
func TestTraverseArray_EmptyArray(t *testing.T) {
	identity := func(n int) (int, error) {
		return n, nil
	}

	result, err := TraverseArray(identity)([]int{})

	require.NoError(t, err)
	assert.Empty(t, result)
	assert.NotNil(t, result)
}

// TestTraverseArray_DifferentTypes tests transformation between different types
func TestTraverseArray_DifferentTypes(t *testing.T) {
	toLength := func(s string) (int, error) {
		if len(s) == 0 {
			return 0, errors.New("empty string")
		}
		return len(s), nil
	}

	result, err := TraverseArray(toLength)([]string{"a", "bb", "ccc"})

	require.NoError(t, err)
	assert.Equal(t, []int{1, 2, 3}, result)
}

// TestTraverseArrayWithIndexG_Success tests successful indexed traversal
func TestTraverseArrayWithIndexG_Success(t *testing.T) {
	annotate := func(i int, s string) (string, error) {
		if len(s) == 0 {
			return "", fmt.Errorf("empty string at index %d", i)
		}
		return fmt.Sprintf("[%d]=%s", i, s), nil
	}

	result, err := TraverseArrayWithIndexG[[]string, []string](annotate)([]string{"a", "b", "c"})

	require.NoError(t, err)
	assert.Equal(t, []string{"[0]=a", "[1]=b", "[2]=c"}, result)
}

// TestTraverseArrayWithIndexG_Error tests error handling with index
func TestTraverseArrayWithIndexG_Error(t *testing.T) {
	annotate := func(i int, s string) (string, error) {
		if len(s) == 0 {
			return "", fmt.Errorf("empty string at index %d", i)
		}
		return fmt.Sprintf("[%d]=%s", i, s), nil
	}

	result, err := TraverseArrayWithIndexG[[]string, []string](annotate)([]string{"a", "", "c"})

	require.Error(t, err)
	assert.Contains(t, err.Error(), "index 1")
	assert.Nil(t, result)
}

// TestTraverseArrayWithIndexG_EmptyArray tests empty array
func TestTraverseArrayWithIndexG_EmptyArray(t *testing.T) {
	annotate := func(i int, s string) (string, error) {
		return fmt.Sprintf("%d:%s", i, s), nil
	}

	result, err := TraverseArrayWithIndexG[[]string, []string](annotate)([]string{})

	require.NoError(t, err)
	assert.Empty(t, result)
	assert.NotNil(t, result)
}

// TestTraverseArrayWithIndexG_IndexValidation tests that indices are correct
func TestTraverseArrayWithIndexG_IndexValidation(t *testing.T) {
	indices := []int{}
	collect := func(i int, s string) (string, error) {
		indices = append(indices, i)
		return s, nil
	}

	_, err := TraverseArrayWithIndexG[[]string, []string](collect)([]string{"a", "b", "c", "d"})

	require.NoError(t, err)
	assert.Equal(t, []int{0, 1, 2, 3}, indices)
}

// TestTraverseArrayWithIndexG_ShortCircuit tests short-circuit behavior
func TestTraverseArrayWithIndexG_ShortCircuit(t *testing.T) {
	maxIndex := -1
	process := func(i int, s string) (string, error) {
		maxIndex = i
		if i == 2 {
			return "", errors.New("stop at index 2")
		}
		return s, nil
	}

	_, err := TraverseArrayWithIndexG[[]string, []string](process)([]string{"a", "b", "c", "d", "e"})

	require.Error(t, err)
	assert.Equal(t, 2, maxIndex, "should stop at index 2")
}

// TestTraverseArrayWithIndexG_CustomSliceType tests with custom slice types
func TestTraverseArrayWithIndexG_CustomSliceType(t *testing.T) {
	type StringSlice []string
	type ResultSlice []string

	annotate := func(i int, s string) (string, error) {
		return fmt.Sprintf("%d:%s", i, s), nil
	}

	input := StringSlice{"x", "y", "z"}
	result, err := TraverseArrayWithIndexG[StringSlice, ResultSlice](annotate)(input)

	require.NoError(t, err)
	assert.Equal(t, ResultSlice{"0:x", "1:y", "2:z"}, result)
}

// TestTraverseArrayWithIndex_Success tests successful indexed traversal
func TestTraverseArrayWithIndex_Success(t *testing.T) {
	check := func(i int, s string) (string, error) {
		if len(s) == 0 {
			return "", fmt.Errorf("empty value at position %d", i)
		}
		return fmt.Sprintf("%d_%s", i, s), nil
	}

	result, err := TraverseArrayWithIndex(check)([]string{"a", "b", "c"})

	require.NoError(t, err)
	assert.Equal(t, []string{"0_a", "1_b", "2_c"}, result)
}

// TestTraverseArrayWithIndex_Error tests error with position info
func TestTraverseArrayWithIndex_Error(t *testing.T) {
	check := func(i int, s string) (string, error) {
		if len(s) == 0 {
			return "", fmt.Errorf("empty value at position %d", i)
		}
		return s, nil
	}

	result, err := TraverseArrayWithIndex(check)([]string{"ok", "ok", "", "ok"})

	require.Error(t, err)
	assert.Contains(t, err.Error(), "position 2")
	assert.Nil(t, result)
}

// TestTraverseArrayWithIndex_TypeTransformation tests transforming types with index
func TestTraverseArrayWithIndex_TypeTransformation(t *testing.T) {
	multiply := func(i int, n int) (int, error) {
		return n * (i + 1), nil
	}

	result, err := TraverseArrayWithIndex(multiply)([]int{10, 20, 30})

	require.NoError(t, err)
	assert.Equal(t, []int{10, 40, 90}, result) // [10*(0+1), 20*(1+1), 30*(2+1)]
}

// TestTraverseArrayWithIndex_EmptyArray tests empty array
func TestTraverseArrayWithIndex_EmptyArray(t *testing.T) {
	process := func(i int, s string) (int, error) {
		return i, nil
	}

	result, err := TraverseArrayWithIndex(process)([]string{})

	require.NoError(t, err)
	assert.Empty(t, result)
	assert.NotNil(t, result)
}

// TestTraverseArrayWithIndex_SingleElement tests single element processing
func TestTraverseArrayWithIndex_SingleElement(t *testing.T) {
	annotate := func(i int, s string) (string, error) {
		return fmt.Sprintf("item_%d:%s", i, s), nil
	}

	result, err := TraverseArrayWithIndex(annotate)([]string{"solo"})

	require.NoError(t, err)
	assert.Equal(t, []string{"item_0:solo"}, result)
}

// TestTraverseArrayWithIndex_ConditionalLogic tests using index for conditional logic
func TestTraverseArrayWithIndex_ConditionalLogic(t *testing.T) {
	// Only accept even indices
	process := func(i int, s string) (string, error) {
		if i%2 != 0 {
			return "", fmt.Errorf("odd index %d not allowed", i)
		}
		return s, nil
	}

	result, err := TraverseArrayWithIndex(process)([]string{"ok", "fail"})

	require.Error(t, err)
	assert.Contains(t, err.Error(), "odd index 1")
	assert.Nil(t, result)
}

// TestTraverseArray_LargeArray tests traversal with a larger array
func TestTraverseArray_LargeArray(t *testing.T) {
	// Create array with 1000 elements
	input := make([]int, 1000)
	for i := range input {
		input[i] = i
	}

	double := func(n int) (int, error) {
		return n * 2, nil
	}

	result, err := TraverseArray(double)(input)

	require.NoError(t, err)
	assert.Len(t, result, 1000)
	assert.Equal(t, 0, result[0])
	assert.Equal(t, 1998, result[999])
}

// TestTraverseArrayG_PreservesOrder tests that order is preserved
func TestTraverseArrayG_PreservesOrder(t *testing.T) {
	reverse := func(s string) (string, error) {
		runes := []rune(s)
		for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
			runes[i], runes[j] = runes[j], runes[i]
		}
		return string(runes), nil
	}

	result, err := TraverseArrayG[[]string, []string](reverse)([]string{"abc", "def", "ghi"})

	require.NoError(t, err)
	assert.Equal(t, []string{"cba", "fed", "ihg"}, result)
}

// TestTraverseArrayWithIndex_BoundaryCheck tests boundary conditions with index
func TestTraverseArrayWithIndex_BoundaryCheck(t *testing.T) {
	// Reject if index exceeds a threshold
	limitedProcess := func(i int, s string) (string, error) {
		if i >= 100 {
			return "", errors.New("index too large")
		}
		return s, nil
	}

	// Should succeed with index < 100
	result, err := TraverseArrayWithIndex(limitedProcess)([]string{"a", "b", "c"})
	require.NoError(t, err)
	assert.Equal(t, []string{"a", "b", "c"}, result)
}
